@n8n/n8n-nodes-langchain 1.87.1 → 1.89.0
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/dist/credentials/MilvusApi.credentials.js +72 -0
- package/dist/credentials/MilvusApi.credentials.js.map +1 -0
- package/dist/credentials/QdrantApi.credentials.js +1 -3
- package/dist/credentials/QdrantApi.credentials.js.map +1 -1
- package/dist/credentials/SearXngApi.credentials.js +44 -0
- package/dist/credentials/SearXngApi.credentials.js.map +1 -0
- package/dist/known/credentials.json +14 -0
- package/dist/known/nodes.json +16 -0
- package/dist/methods/defined.json +3 -0
- package/dist/methods/referenced.json +3 -0
- package/dist/nodes/agents/Agent/Agent.node.js +1 -1
- package/dist/nodes/agents/Agent/Agent.node.js.map +1 -1
- package/dist/nodes/agents/Agent/agents/ToolsAgent/execute.js +11 -5
- package/dist/nodes/agents/Agent/agents/ToolsAgent/execute.js.map +1 -1
- package/dist/nodes/llms/LmChatXAiGrok/LmChatXAiGrok.node.js +6 -3
- package/dist/nodes/llms/LmChatXAiGrok/LmChatXAiGrok.node.js.map +1 -1
- package/dist/nodes/mcp/McpClientTool/McpClientTool.node.js +258 -0
- package/dist/nodes/mcp/McpClientTool/McpClientTool.node.js.map +1 -0
- package/dist/nodes/mcp/McpClientTool/loadOptions.js +51 -0
- package/dist/nodes/mcp/McpClientTool/loadOptions.js.map +1 -0
- package/dist/nodes/mcp/McpClientTool/types.js +17 -0
- package/dist/nodes/mcp/McpClientTool/types.js.map +1 -0
- package/dist/nodes/mcp/McpClientTool/utils.js +192 -0
- package/dist/nodes/mcp/McpClientTool/utils.js.map +1 -0
- package/dist/nodes/mcp/McpTrigger/FlushingSSEServerTransport.js +39 -0
- package/dist/nodes/mcp/McpTrigger/FlushingSSEServerTransport.js.map +1 -0
- package/dist/nodes/mcp/McpTrigger/McpServer.js +179 -0
- package/dist/nodes/mcp/McpTrigger/McpServer.js.map +1 -0
- package/dist/nodes/mcp/McpTrigger/McpTrigger.node.js +181 -0
- package/dist/nodes/mcp/McpTrigger/McpTrigger.node.js.map +1 -0
- package/dist/nodes/mcp/mcp.dark.svg +7 -0
- package/dist/nodes/mcp/mcp.svg +7 -0
- package/dist/nodes/tools/ToolSearXng/ToolSearXng.node.js +136 -0
- package/dist/nodes/tools/ToolSearXng/ToolSearXng.node.js.map +1 -0
- package/dist/nodes/tools/ToolSearXng/searXng.svg +1 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/VectorStoreMilvus.node.js +106 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/VectorStoreMilvus.node.js.map +1 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/milvus-icon-black.svg +1 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/milvus-icon-white.svg +1 -0
- package/dist/nodes/vector_store/VectorStoreQdrant/Qdrant.utils.js +54 -0
- package/dist/nodes/vector_store/VectorStoreQdrant/Qdrant.utils.js.map +1 -0
- package/dist/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.js +5 -4
- package/dist/nodes/vector_store/VectorStoreQdrant/VectorStoreQdrant.node.js.map +1 -1
- package/dist/nodes/vector_store/shared/createVectorStoreNode/methods/listSearch.js +18 -5
- package/dist/nodes/vector_store/shared/createVectorStoreNode/methods/listSearch.js.map +1 -1
- package/dist/nodes/vector_store/shared/descriptions.js +24 -0
- package/dist/nodes/vector_store/shared/descriptions.js.map +1 -1
- package/dist/types/credentials.json +3 -1
- package/dist/types/nodes.json +5 -1
- package/dist/utils/helpers.js +7 -1
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/logWrapper.js +9 -2
- package/dist/utils/logWrapper.js.map +1 -1
- package/package.json +13 -5
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __typeError = (msg) => {
|
|
7
|
+
throw TypeError(msg);
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
23
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
26
|
+
var McpServer_exports = {};
|
|
27
|
+
__export(McpServer_exports, {
|
|
28
|
+
McpServer: () => McpServer,
|
|
29
|
+
McpServerSingleton: () => McpServerSingleton
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(McpServer_exports);
|
|
32
|
+
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
33
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
34
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
35
|
+
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
36
|
+
var import_FlushingSSEServerTransport = require("./FlushingSSEServerTransport");
|
|
37
|
+
var _instance;
|
|
38
|
+
function wasToolCall(body) {
|
|
39
|
+
try {
|
|
40
|
+
const message = JSON.parse(body);
|
|
41
|
+
const parsedMessage = import_types.JSONRPCMessageSchema.parse(message);
|
|
42
|
+
return "method" in parsedMessage && "id" in parsedMessage && parsedMessage?.method === import_types.CallToolRequestSchema.shape.method.value;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
class McpServer {
|
|
48
|
+
constructor(logger) {
|
|
49
|
+
this.servers = {};
|
|
50
|
+
this.transports = {};
|
|
51
|
+
this.tools = {};
|
|
52
|
+
this.resolveFunctions = {};
|
|
53
|
+
this.logger = logger;
|
|
54
|
+
this.logger.debug("MCP Server created");
|
|
55
|
+
}
|
|
56
|
+
async connectTransport(postUrl, resp) {
|
|
57
|
+
const transport = new import_FlushingSSEServerTransport.FlushingSSEServerTransport(postUrl, resp);
|
|
58
|
+
const server = this.setUpServer();
|
|
59
|
+
const { sessionId } = transport;
|
|
60
|
+
this.transports[sessionId] = transport;
|
|
61
|
+
this.servers[sessionId] = server;
|
|
62
|
+
resp.on("close", async () => {
|
|
63
|
+
this.logger.debug(`Deleting transport for ${sessionId}`);
|
|
64
|
+
delete this.tools[sessionId];
|
|
65
|
+
delete this.resolveFunctions[sessionId];
|
|
66
|
+
delete this.transports[sessionId];
|
|
67
|
+
delete this.servers[sessionId];
|
|
68
|
+
});
|
|
69
|
+
await server.connect(transport);
|
|
70
|
+
if (resp.flush) {
|
|
71
|
+
resp.flush();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async handlePostMessage(req, resp, connectedTools) {
|
|
75
|
+
const sessionId = req.query.sessionId;
|
|
76
|
+
const transport = this.transports[sessionId];
|
|
77
|
+
this.tools[sessionId] = connectedTools;
|
|
78
|
+
if (transport) {
|
|
79
|
+
await new Promise(async (resolve) => {
|
|
80
|
+
this.resolveFunctions[sessionId] = resolve;
|
|
81
|
+
await transport.handlePostMessage(req, resp, req.rawBody.toString());
|
|
82
|
+
});
|
|
83
|
+
delete this.resolveFunctions[sessionId];
|
|
84
|
+
} else {
|
|
85
|
+
this.logger.warn(`No transport found for session ${sessionId}`);
|
|
86
|
+
resp.status(401).send("No transport found for sessionId");
|
|
87
|
+
}
|
|
88
|
+
if (resp.flush) {
|
|
89
|
+
resp.flush();
|
|
90
|
+
}
|
|
91
|
+
delete this.tools[sessionId];
|
|
92
|
+
return wasToolCall(req.rawBody.toString());
|
|
93
|
+
}
|
|
94
|
+
setUpServer() {
|
|
95
|
+
const server = new import_server.Server(
|
|
96
|
+
{
|
|
97
|
+
name: "n8n-mcp-server",
|
|
98
|
+
version: "0.1.0"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
capabilities: { tools: {} }
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
server.setRequestHandler(import_types.ListToolsRequestSchema, async (_, extra) => {
|
|
105
|
+
if (!extra.sessionId) {
|
|
106
|
+
throw new import_n8n_workflow.OperationalError("Require a sessionId for the listing of tools");
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
tools: this.tools[extra.sessionId].map((tool) => {
|
|
110
|
+
return {
|
|
111
|
+
name: tool.name,
|
|
112
|
+
description: tool.description,
|
|
113
|
+
inputSchema: (0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema)
|
|
114
|
+
};
|
|
115
|
+
})
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
server.setRequestHandler(import_types.CallToolRequestSchema, async (request, extra) => {
|
|
119
|
+
if (!request.params?.name || !request.params?.arguments) {
|
|
120
|
+
throw new import_n8n_workflow.OperationalError("Require a name and arguments for the tool call");
|
|
121
|
+
}
|
|
122
|
+
if (!extra.sessionId) {
|
|
123
|
+
throw new import_n8n_workflow.OperationalError("Require a sessionId for the tool call");
|
|
124
|
+
}
|
|
125
|
+
const requestedTool = this.tools[extra.sessionId].find(
|
|
126
|
+
(tool) => tool.name === request.params.name
|
|
127
|
+
);
|
|
128
|
+
if (!requestedTool) {
|
|
129
|
+
throw new import_n8n_workflow.OperationalError("Tool not found");
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const result = await requestedTool.invoke(request.params.arguments);
|
|
133
|
+
this.resolveFunctions[extra.sessionId]();
|
|
134
|
+
this.logger.debug(`Got request for ${requestedTool.name}, and executed it.`);
|
|
135
|
+
if (typeof result === "object") {
|
|
136
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
137
|
+
}
|
|
138
|
+
if (typeof result === "string") {
|
|
139
|
+
return { content: [{ type: "text", text: result }] };
|
|
140
|
+
}
|
|
141
|
+
return { content: [{ type: "text", text: String(result) }] };
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.logger.error(`Error while executing Tool ${requestedTool.name}: ${error}`);
|
|
144
|
+
return { isError: true, content: [{ type: "text", text: `Error: ${error.message}` }] };
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
server.onclose = () => {
|
|
148
|
+
this.logger.debug("Closing MCP Server");
|
|
149
|
+
};
|
|
150
|
+
server.onerror = (error) => {
|
|
151
|
+
this.logger.error(`MCP Error: ${error}`);
|
|
152
|
+
};
|
|
153
|
+
return server;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const _McpServerSingleton = class _McpServerSingleton {
|
|
157
|
+
constructor(logger) {
|
|
158
|
+
this._serverData = new McpServer(logger);
|
|
159
|
+
}
|
|
160
|
+
static instance(logger) {
|
|
161
|
+
if (!__privateGet(_McpServerSingleton, _instance)) {
|
|
162
|
+
__privateSet(_McpServerSingleton, _instance, new _McpServerSingleton(logger));
|
|
163
|
+
logger.debug("Created singleton for MCP Servers");
|
|
164
|
+
}
|
|
165
|
+
return __privateGet(_McpServerSingleton, _instance).serverData;
|
|
166
|
+
}
|
|
167
|
+
get serverData() {
|
|
168
|
+
return this._serverData;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
_instance = new WeakMap();
|
|
172
|
+
__privateAdd(_McpServerSingleton, _instance);
|
|
173
|
+
let McpServerSingleton = _McpServerSingleton;
|
|
174
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
175
|
+
0 && (module.exports = {
|
|
176
|
+
McpServer,
|
|
177
|
+
McpServerSingleton
|
|
178
|
+
});
|
|
179
|
+
//# sourceMappingURL=McpServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/mcp/McpTrigger/McpServer.ts"],"sourcesContent":["import type { Tool } from '@langchain/core/tools';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\nimport {\n\tJSONRPCMessageSchema,\n\tListToolsRequestSchema,\n\tCallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type * as express from 'express';\nimport { OperationalError, type Logger } from 'n8n-workflow';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\n\nimport { FlushingSSEServerTransport } from './FlushingSSEServerTransport';\nimport type { CompressionResponse } from './FlushingSSEServerTransport';\n\n/**\n * Parses the JSONRPC message and checks whether the method used was a tool\n * call. This is necessary in order to not have executions for listing tools\n * and other commands sent by the MCP client\n */\nfunction wasToolCall(body: string) {\n\ttry {\n\t\tconst message: unknown = JSON.parse(body);\n\t\tconst parsedMessage: JSONRPCMessage = JSONRPCMessageSchema.parse(message);\n\t\treturn (\n\t\t\t'method' in parsedMessage &&\n\t\t\t'id' in parsedMessage &&\n\t\t\tparsedMessage?.method === CallToolRequestSchema.shape.method.value\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport class McpServer {\n\tservers: { [sessionId: string]: Server } = {};\n\n\ttransports: { [sessionId: string]: FlushingSSEServerTransport } = {};\n\n\tlogger: Logger;\n\n\tprivate tools: { [sessionId: string]: Tool[] } = {};\n\n\tprivate resolveFunctions: { [sessionId: string]: CallableFunction } = {};\n\n\tconstructor(logger: Logger) {\n\t\tthis.logger = logger;\n\t\tthis.logger.debug('MCP Server created');\n\t}\n\n\tasync connectTransport(postUrl: string, resp: CompressionResponse): Promise<void> {\n\t\tconst transport = new FlushingSSEServerTransport(postUrl, resp);\n\t\tconst server = this.setUpServer();\n\t\tconst { sessionId } = transport;\n\t\tthis.transports[sessionId] = transport;\n\t\tthis.servers[sessionId] = server;\n\n\t\tresp.on('close', async () => {\n\t\t\tthis.logger.debug(`Deleting transport for ${sessionId}`);\n\t\t\tdelete this.tools[sessionId];\n\t\t\tdelete this.resolveFunctions[sessionId];\n\t\t\tdelete this.transports[sessionId];\n\t\t\tdelete this.servers[sessionId];\n\t\t});\n\n\t\tawait server.connect(transport);\n\n\t\t// Make sure we flush the compression middleware, so that it's not waiting for more content to be added to the buffer\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\t}\n\n\tasync handlePostMessage(req: express.Request, resp: CompressionResponse, connectedTools: Tool[]) {\n\t\tconst sessionId = req.query.sessionId as string;\n\t\tconst transport = this.transports[sessionId];\n\t\tthis.tools[sessionId] = connectedTools;\n\t\tif (transport) {\n\t\t\t// We need to add a promise here because the `handlePostMessage` will send something to the\n\t\t\t// MCP Server, that will run in a different context. This means that the return will happen\n\t\t\t// almost immediately, and will lead to marking the sub-node as \"running\" in the final execution\n\t\t\tawait new Promise(async (resolve) => {\n\t\t\t\tthis.resolveFunctions[sessionId] = resolve;\n\t\t\t\tawait transport.handlePostMessage(req, resp, req.rawBody.toString());\n\t\t\t});\n\t\t\tdelete this.resolveFunctions[sessionId];\n\t\t} else {\n\t\t\tthis.logger.warn(`No transport found for session ${sessionId}`);\n\t\t\tresp.status(401).send('No transport found for sessionId');\n\t\t}\n\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\n\t\tdelete this.tools[sessionId]; // Clean up to avoid keeping all tools in memory\n\n\t\treturn wasToolCall(req.rawBody.toString());\n\t}\n\n\tsetUpServer(): Server {\n\t\tconst server = new Server(\n\t\t\t{\n\t\t\t\tname: 'n8n-mcp-server',\n\t\t\t\tversion: '0.1.0',\n\t\t\t},\n\t\t\t{\n\t\t\t\tcapabilities: { tools: {} },\n\t\t\t},\n\t\t);\n\n\t\tserver.setRequestHandler(ListToolsRequestSchema, async (_, extra: RequestHandlerExtra) => {\n\t\t\tif (!extra.sessionId) {\n\t\t\t\tthrow new OperationalError('Require a sessionId for the listing of tools');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttools: this.tools[extra.sessionId].map((tool) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\tinputSchema: zodToJsonSchema(tool.schema),\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t};\n\t\t});\n\n\t\tserver.setRequestHandler(CallToolRequestSchema, async (request, extra: RequestHandlerExtra) => {\n\t\t\tif (!request.params?.name || !request.params?.arguments) {\n\t\t\t\tthrow new OperationalError('Require a name and arguments for the tool call');\n\t\t\t}\n\t\t\tif (!extra.sessionId) {\n\t\t\t\tthrow new OperationalError('Require a sessionId for the tool call');\n\t\t\t}\n\n\t\t\tconst requestedTool: Tool | undefined = this.tools[extra.sessionId].find(\n\t\t\t\t(tool) => tool.name === request.params.name,\n\t\t\t);\n\t\t\tif (!requestedTool) {\n\t\t\t\tthrow new OperationalError('Tool not found');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = await requestedTool.invoke(request.params.arguments);\n\n\t\t\t\tthis.resolveFunctions[extra.sessionId]();\n\n\t\t\t\tthis.logger.debug(`Got request for ${requestedTool.name}, and executed it.`);\n\n\t\t\t\tif (typeof result === 'object') {\n\t\t\t\t\treturn { content: [{ type: 'text', text: JSON.stringify(result) }] };\n\t\t\t\t}\n\t\t\t\tif (typeof result === 'string') {\n\t\t\t\t\treturn { content: [{ type: 'text', text: result }] };\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: 'text', text: String(result) }] };\n\t\t\t} catch (error) {\n\t\t\t\tthis.logger.error(`Error while executing Tool ${requestedTool.name}: ${error}`);\n\t\t\t\treturn { isError: true, content: [{ type: 'text', text: `Error: ${error.message}` }] };\n\t\t\t}\n\t\t});\n\n\t\tserver.onclose = () => {\n\t\t\tthis.logger.debug('Closing MCP Server');\n\t\t};\n\t\tserver.onerror = (error: unknown) => {\n\t\t\tthis.logger.error(`MCP Error: ${error}`);\n\t\t};\n\t\treturn server;\n\t}\n}\n\n/**\n * This singleton is shared across the instance, making sure we only have one server to worry about.\n * It needs to stay in memory to keep track of the long-lived connections.\n * It requires a logger at first creation to set everything up.\n */\nexport class McpServerSingleton {\n\tstatic #instance: McpServerSingleton;\n\n\tprivate _serverData: McpServer;\n\n\tprivate constructor(logger: Logger) {\n\t\tthis._serverData = new McpServer(logger);\n\t}\n\n\tstatic instance(logger: Logger): McpServer {\n\t\tif (!McpServerSingleton.#instance) {\n\t\t\tMcpServerSingleton.#instance = new McpServerSingleton(logger);\n\t\t\tlogger.debug('Created singleton for MCP Servers');\n\t\t}\n\n\t\treturn McpServerSingleton.#instance.serverData;\n\t}\n\n\tget serverData() {\n\t\treturn this._serverData;\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var McpTrigger_node_exports = {};
|
|
20
|
+
__export(McpTrigger_node_exports, {
|
|
21
|
+
McpTrigger: () => McpTrigger
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(McpTrigger_node_exports);
|
|
24
|
+
var import_error = require("n8n-nodes-base/dist/nodes/Webhook/error");
|
|
25
|
+
var import_utils = require("n8n-nodes-base/dist/nodes/Webhook/utils");
|
|
26
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
27
|
+
var import_helpers = require("../../../utils/helpers");
|
|
28
|
+
var import_McpServer = require("./McpServer");
|
|
29
|
+
const MCP_SSE_SETUP_PATH = "sse";
|
|
30
|
+
const MCP_SSE_MESSAGES_PATH = "messages";
|
|
31
|
+
class McpTrigger extends import_n8n_workflow.Node {
|
|
32
|
+
constructor() {
|
|
33
|
+
super(...arguments);
|
|
34
|
+
this.description = {
|
|
35
|
+
displayName: "MCP Server Trigger",
|
|
36
|
+
name: "mcpTrigger",
|
|
37
|
+
icon: {
|
|
38
|
+
light: "file:../mcp.svg",
|
|
39
|
+
dark: "file:../mcp.dark.svg"
|
|
40
|
+
},
|
|
41
|
+
group: ["trigger"],
|
|
42
|
+
version: 1,
|
|
43
|
+
description: "Expose n8n tools as an MCP Server endpoint",
|
|
44
|
+
activationMessage: "You can now connect your MCP Clients to the SSE URL.",
|
|
45
|
+
defaults: {
|
|
46
|
+
name: "MCP Server Trigger"
|
|
47
|
+
},
|
|
48
|
+
codex: {
|
|
49
|
+
categories: ["AI", "Core Nodes"],
|
|
50
|
+
subcategories: {
|
|
51
|
+
AI: ["Root Nodes", "Model Context Protocol"],
|
|
52
|
+
"Core Nodes": ["Other Trigger Nodes"]
|
|
53
|
+
},
|
|
54
|
+
alias: ["Model Context Protocol", "MCP Server"],
|
|
55
|
+
resources: {
|
|
56
|
+
primaryDocumentation: [
|
|
57
|
+
{
|
|
58
|
+
url: "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.mcptrigger/"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
triggerPanel: {
|
|
64
|
+
header: "Listen for MCP events",
|
|
65
|
+
executionsHelp: {
|
|
66
|
+
inactive: "This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. <a data-key='activate'>Activate</a> the workflow, then make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.",
|
|
67
|
+
active: "This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. Since your workflow is activated, you can make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor."
|
|
68
|
+
},
|
|
69
|
+
activationHint: "Once you\u2019ve finished building your workflow, run it without having to click this button by using the production URL."
|
|
70
|
+
},
|
|
71
|
+
inputs: [
|
|
72
|
+
{
|
|
73
|
+
type: import_n8n_workflow.NodeConnectionTypes.AiTool,
|
|
74
|
+
displayName: "Tools"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
outputs: [],
|
|
78
|
+
credentials: [
|
|
79
|
+
{
|
|
80
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-credentials-name-unsuffixed
|
|
81
|
+
name: "httpBearerAuth",
|
|
82
|
+
required: true,
|
|
83
|
+
displayOptions: {
|
|
84
|
+
show: {
|
|
85
|
+
authentication: ["bearerAuth"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "httpHeaderAuth",
|
|
91
|
+
required: true,
|
|
92
|
+
displayOptions: {
|
|
93
|
+
show: {
|
|
94
|
+
authentication: ["headerAuth"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
properties: [
|
|
100
|
+
{
|
|
101
|
+
displayName: "Authentication",
|
|
102
|
+
name: "authentication",
|
|
103
|
+
type: "options",
|
|
104
|
+
options: [
|
|
105
|
+
{ name: "None", value: "none" },
|
|
106
|
+
{ name: "Bearer Auth", value: "bearerAuth" },
|
|
107
|
+
{ name: "Header Auth", value: "headerAuth" }
|
|
108
|
+
],
|
|
109
|
+
default: "none",
|
|
110
|
+
description: "The way to authenticate"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
displayName: "Path",
|
|
114
|
+
name: "path",
|
|
115
|
+
type: "string",
|
|
116
|
+
default: "",
|
|
117
|
+
placeholder: "webhook",
|
|
118
|
+
required: true,
|
|
119
|
+
description: "The base path for this MCP server"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
webhooks: [
|
|
123
|
+
{
|
|
124
|
+
name: "setup",
|
|
125
|
+
httpMethod: "GET",
|
|
126
|
+
responseMode: "onReceived",
|
|
127
|
+
isFullPath: true,
|
|
128
|
+
path: `={{$parameter["path"]}}/${MCP_SSE_SETUP_PATH}`,
|
|
129
|
+
nodeType: "mcp",
|
|
130
|
+
ndvHideMethod: true,
|
|
131
|
+
ndvHideUrl: false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "default",
|
|
135
|
+
httpMethod: "POST",
|
|
136
|
+
responseMode: "onReceived",
|
|
137
|
+
isFullPath: true,
|
|
138
|
+
path: `={{$parameter["path"]}}/${MCP_SSE_MESSAGES_PATH}`,
|
|
139
|
+
nodeType: "mcp",
|
|
140
|
+
ndvHideMethod: true,
|
|
141
|
+
ndvHideUrl: true
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async webhook(context) {
|
|
147
|
+
const webhookName = context.getWebhookName();
|
|
148
|
+
const req = context.getRequestObject();
|
|
149
|
+
const resp = context.getResponseObject();
|
|
150
|
+
try {
|
|
151
|
+
await (0, import_utils.validateWebhookAuthentication)(context, "authentication");
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error instanceof import_error.WebhookAuthorizationError) {
|
|
154
|
+
resp.writeHead(error.responseCode);
|
|
155
|
+
resp.end(error.message);
|
|
156
|
+
return { noWebhookResponse: true };
|
|
157
|
+
}
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
const mcpServer = import_McpServer.McpServerSingleton.instance(context.logger);
|
|
161
|
+
if (webhookName === "setup") {
|
|
162
|
+
const postUrl = req.path.replace(
|
|
163
|
+
new RegExp(`/${MCP_SSE_SETUP_PATH}$`),
|
|
164
|
+
`/${MCP_SSE_MESSAGES_PATH}`
|
|
165
|
+
);
|
|
166
|
+
await mcpServer.connectTransport(postUrl, resp);
|
|
167
|
+
return { noWebhookResponse: true };
|
|
168
|
+
} else if (webhookName === "default") {
|
|
169
|
+
const connectedTools = await (0, import_helpers.getConnectedTools)(context, true);
|
|
170
|
+
const wasToolCall = await mcpServer.handlePostMessage(req, resp, connectedTools);
|
|
171
|
+
if (wasToolCall) return { noWebhookResponse: true, workflowData: [[{ json: {} }]] };
|
|
172
|
+
return { noWebhookResponse: true };
|
|
173
|
+
}
|
|
174
|
+
return { workflowData: [[{ json: {} }]] };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
178
|
+
0 && (module.exports = {
|
|
179
|
+
McpTrigger
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=McpTrigger.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/mcp/McpTrigger/McpTrigger.node.ts"],"sourcesContent":["import { WebhookAuthorizationError } from 'n8n-nodes-base/dist/nodes/Webhook/error';\nimport { validateWebhookAuthentication } from 'n8n-nodes-base/dist/nodes/Webhook/utils';\nimport type { INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';\nimport { NodeConnectionTypes, Node } from 'n8n-workflow';\n\nimport { getConnectedTools } from '@utils/helpers';\n\nimport type { CompressionResponse } from './FlushingSSEServerTransport';\nimport { McpServerSingleton } from './McpServer';\nimport type { McpServer } from './McpServer';\n\nconst MCP_SSE_SETUP_PATH = 'sse';\nconst MCP_SSE_MESSAGES_PATH = 'messages';\n\nexport class McpTrigger extends Node {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'MCP Server Trigger',\n\t\tname: 'mcpTrigger',\n\t\ticon: {\n\t\t\tlight: 'file:../mcp.svg',\n\t\t\tdark: 'file:../mcp.dark.svg',\n\t\t},\n\t\tgroup: ['trigger'],\n\t\tversion: 1,\n\t\tdescription: 'Expose n8n tools as an MCP Server endpoint',\n\t\tactivationMessage: 'You can now connect your MCP Clients to the SSE URL.',\n\t\tdefaults: {\n\t\t\tname: 'MCP Server Trigger',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI', 'Core Nodes'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Root Nodes', 'Model Context Protocol'],\n\t\t\t\t'Core Nodes': ['Other Trigger Nodes'],\n\t\t\t},\n\t\t\talias: ['Model Context Protocol', 'MCP Server'],\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.mcptrigger/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\ttriggerPanel: {\n\t\t\theader: 'Listen for MCP events',\n\t\t\texecutionsHelp: {\n\t\t\t\tinactive:\n\t\t\t\t\t\"This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. <a data-key='activate'>Activate</a> the workflow, then make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.\",\n\t\t\t\tactive:\n\t\t\t\t\t\"This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. Since your workflow is activated, you can make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.\",\n\t\t\t},\n\t\t\tactivationHint:\n\t\t\t\t'Once you’ve finished building your workflow, run it without having to click this button by using the production URL.',\n\t\t},\n\t\tinputs: [\n\t\t\t{\n\t\t\t\ttype: NodeConnectionTypes.AiTool,\n\t\t\t\tdisplayName: 'Tools',\n\t\t\t},\n\t\t],\n\t\toutputs: [],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-credentials-name-unsuffixed\n\t\t\t\tname: 'httpBearerAuth',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tauthentication: ['bearerAuth'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'httpHeaderAuth',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tauthentication: ['headerAuth'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tproperties: [\n\t\t\t{\n\t\t\t\tdisplayName: 'Authentication',\n\t\t\t\tname: 'authentication',\n\t\t\t\ttype: 'options',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'None', value: 'none' },\n\t\t\t\t\t{ name: 'Bearer Auth', value: 'bearerAuth' },\n\t\t\t\t\t{ name: 'Header Auth', value: 'headerAuth' },\n\t\t\t\t],\n\t\t\t\tdefault: 'none',\n\t\t\t\tdescription: 'The way to authenticate',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Path',\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'webhook',\n\t\t\t\trequired: true,\n\t\t\t\tdescription: 'The base path for this MCP server',\n\t\t\t},\n\t\t],\n\t\twebhooks: [\n\t\t\t{\n\t\t\t\tname: 'setup',\n\t\t\t\thttpMethod: 'GET',\n\t\t\t\tresponseMode: 'onReceived',\n\t\t\t\tisFullPath: true,\n\t\t\t\tpath: `={{$parameter[\"path\"]}}/${MCP_SSE_SETUP_PATH}`,\n\t\t\t\tnodeType: 'mcp',\n\t\t\t\tndvHideMethod: true,\n\t\t\t\tndvHideUrl: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'default',\n\t\t\t\thttpMethod: 'POST',\n\t\t\t\tresponseMode: 'onReceived',\n\t\t\t\tisFullPath: true,\n\t\t\t\tpath: `={{$parameter[\"path\"]}}/${MCP_SSE_MESSAGES_PATH}`,\n\t\t\t\tnodeType: 'mcp',\n\t\t\t\tndvHideMethod: true,\n\t\t\t\tndvHideUrl: true,\n\t\t\t},\n\t\t],\n\t};\n\n\tasync webhook(context: IWebhookFunctions): Promise<IWebhookResponseData> {\n\t\tconst webhookName = context.getWebhookName();\n\t\tconst req = context.getRequestObject();\n\t\tconst resp = context.getResponseObject() as unknown as CompressionResponse;\n\n\t\ttry {\n\t\t\tawait validateWebhookAuthentication(context, 'authentication');\n\t\t} catch (error) {\n\t\t\tif (error instanceof WebhookAuthorizationError) {\n\t\t\t\tresp.writeHead(error.responseCode);\n\t\t\t\tresp.end(error.message);\n\t\t\t\treturn { noWebhookResponse: true };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst mcpServer: McpServer = McpServerSingleton.instance(context.logger);\n\n\t\tif (webhookName === 'setup') {\n\t\t\t// Sets up the transport and opens the long-lived connection. This resp\n\t\t\t// will stay streaming, and is the channel that sends the events\n\t\t\tconst postUrl = req.path.replace(\n\t\t\t\tnew RegExp(`/${MCP_SSE_SETUP_PATH}$`),\n\t\t\t\t`/${MCP_SSE_MESSAGES_PATH}`,\n\t\t\t);\n\t\t\tawait mcpServer.connectTransport(postUrl, resp);\n\n\t\t\treturn { noWebhookResponse: true };\n\t\t} else if (webhookName === 'default') {\n\t\t\t// This is the command-channel, and is actually executing the tools. This\n\t\t\t// sends the response back through the long-lived connection setup in the\n\t\t\t// 'setup' call\n\t\t\tconst connectedTools = await getConnectedTools(context, true);\n\n\t\t\tconst wasToolCall = await mcpServer.handlePostMessage(req, resp, connectedTools);\n\n\t\t\tif (wasToolCall) return { noWebhookResponse: true, workflowData: [[{ json: {} }]] };\n\t\t\treturn { noWebhookResponse: true };\n\t\t}\n\n\t\treturn { workflowData: [[{ json: {} }]] };\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg width="180" height="180" viewBox="0 0 195 195" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g stroke="#fff" stroke-width="12" stroke-linecap="round">
|
|
3
|
+
<path d="M25 97.8528L92.8823 29.9706C102.255 20.598 117.451 20.598 126.823 29.9706V29.9706C136.196 39.3431 136.196 54.5391 126.823 63.9117L75.5581 115.177"/>
|
|
4
|
+
<path d="M76.2653 114.47L126.823 63.9117C136.196 54.5391 151.392 54.5391 160.765 63.9117L161.118 64.2652C170.491 73.6378 170.491 88.8338 161.118 98.2063L99.7248 159.6C96.6006 162.724 96.6006 167.789 99.7248 170.913L112.331 183.52"/>
|
|
5
|
+
<path d="M109.853 46.9411L59.6482 97.1457C50.2757 106.518 50.2757 121.714 59.6482 131.087V131.087C69.0208 140.459 84.2168 140.459 93.5894 131.087L143.794 80.8822"/>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg width="180" height="180" viewBox="0 0 195 195" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g stroke="#000" stroke-width="12" stroke-linecap="round">
|
|
3
|
+
<path d="M25 97.8528L92.8823 29.9706C102.255 20.598 117.451 20.598 126.823 29.9706V29.9706C136.196 39.3431 136.196 54.5391 126.823 63.9117L75.5581 115.177"/>
|
|
4
|
+
<path d="M76.2653 114.47L126.823 63.9117C136.196 54.5391 151.392 54.5391 160.765 63.9117L161.118 64.2652C170.491 73.6378 170.491 88.8338 161.118 98.2063L99.7248 159.6C96.6006 162.724 96.6006 167.789 99.7248 170.913L112.331 183.52"/>
|
|
5
|
+
<path d="M109.853 46.9411L59.6482 97.1457C50.2757 106.518 50.2757 121.714 59.6482 131.087V131.087C69.0208 140.459 84.2168 140.459 93.5894 131.087L143.794 80.8822"/>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var ToolSearXng_node_exports = {};
|
|
20
|
+
__export(ToolSearXng_node_exports, {
|
|
21
|
+
ToolSearXng: () => ToolSearXng
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(ToolSearXng_node_exports);
|
|
24
|
+
var import_searxng_search = require("@langchain/community/tools/searxng_search");
|
|
25
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
26
|
+
var import_logWrapper = require("../../../utils/logWrapper");
|
|
27
|
+
var import_sharedFields = require("../../../utils/sharedFields");
|
|
28
|
+
class ToolSearXng {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.description = {
|
|
31
|
+
displayName: "SearXNG",
|
|
32
|
+
name: "toolSearXng",
|
|
33
|
+
icon: "file:searXng.svg",
|
|
34
|
+
group: ["transform"],
|
|
35
|
+
version: 1,
|
|
36
|
+
description: "Search in SearXNG",
|
|
37
|
+
defaults: {
|
|
38
|
+
name: "SearXNG"
|
|
39
|
+
},
|
|
40
|
+
codex: {
|
|
41
|
+
categories: ["AI"],
|
|
42
|
+
subcategories: {
|
|
43
|
+
AI: ["Tools"],
|
|
44
|
+
Tools: ["Other Tools"]
|
|
45
|
+
},
|
|
46
|
+
resources: {
|
|
47
|
+
primaryDocumentation: [
|
|
48
|
+
{
|
|
49
|
+
url: "https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolsearxng"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
inputs: [],
|
|
55
|
+
outputs: [import_n8n_workflow.NodeConnectionTypes.AiTool],
|
|
56
|
+
outputNames: ["Tool"],
|
|
57
|
+
credentials: [
|
|
58
|
+
{
|
|
59
|
+
name: "searXngApi",
|
|
60
|
+
required: true
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
properties: [
|
|
64
|
+
(0, import_sharedFields.getConnectionHintNoticeField)([import_n8n_workflow.NodeConnectionTypes.AiAgent]),
|
|
65
|
+
{
|
|
66
|
+
displayName: "Options",
|
|
67
|
+
name: "options",
|
|
68
|
+
type: "collection",
|
|
69
|
+
placeholder: "Add Option",
|
|
70
|
+
default: {},
|
|
71
|
+
options: [
|
|
72
|
+
{
|
|
73
|
+
displayName: "Number of Results",
|
|
74
|
+
name: "numResults",
|
|
75
|
+
type: "number",
|
|
76
|
+
default: 10
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
displayName: "Search Page Number",
|
|
80
|
+
name: "pageNumber",
|
|
81
|
+
type: "number",
|
|
82
|
+
default: 1
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
displayName: "Language",
|
|
86
|
+
name: "language",
|
|
87
|
+
type: "string",
|
|
88
|
+
default: "en",
|
|
89
|
+
description: 'Defines the language to use. It\'s a two-letter language code. (e.g., `en` for English, `es` for Spanish, or `fr` for French). Head to <a href="https://docs.searxng.org/user/search-syntax.html#select-language">SearXNG search syntax page</a> for more info.'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
displayName: "Safe Search",
|
|
93
|
+
name: "safesearch",
|
|
94
|
+
type: "options",
|
|
95
|
+
options: [
|
|
96
|
+
{
|
|
97
|
+
name: "None",
|
|
98
|
+
value: 0
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "Moderate",
|
|
102
|
+
value: 1
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "Strict",
|
|
106
|
+
value: 2
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
default: 0,
|
|
110
|
+
description: "Filter search results of engines which support safe search"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async supplyData(itemIndex) {
|
|
118
|
+
const credentials = await this.getCredentials("searXngApi");
|
|
119
|
+
const options = this.getNodeParameter("options", itemIndex);
|
|
120
|
+
const tool = new import_searxng_search.SearxngSearch({
|
|
121
|
+
apiBase: credentials.apiUrl,
|
|
122
|
+
headers: {
|
|
123
|
+
Accept: "application/json"
|
|
124
|
+
},
|
|
125
|
+
params: options
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
response: (0, import_logWrapper.logWrapper)(tool, this)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
133
|
+
0 && (module.exports = {
|
|
134
|
+
ToolSearXng
|
|
135
|
+
});
|
|
136
|
+
//# sourceMappingURL=ToolSearXng.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/tools/ToolSearXng/ToolSearXng.node.ts"],"sourcesContent":["import { SearxngSearch } from '@langchain/community/tools/searxng_search';\nimport { NodeConnectionTypes } from 'n8n-workflow';\nimport type {\n\tINodeType,\n\tINodeTypeDescription,\n\tISupplyDataFunctions,\n\tSupplyData,\n} from 'n8n-workflow';\n\nimport { logWrapper } from '@utils/logWrapper';\nimport { getConnectionHintNoticeField } from '@utils/sharedFields';\n\ntype Options = {\n\tnumResults: number;\n\tpageNumber: number;\n\tlanguage: string;\n\tsafesearch: 0 | 1 | 2;\n};\n\nexport class ToolSearXng implements INodeType {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'SearXNG',\n\t\tname: 'toolSearXng',\n\t\ticon: 'file:searXng.svg',\n\t\tgroup: ['transform'],\n\t\tversion: 1,\n\t\tdescription: 'Search in SearXNG',\n\t\tdefaults: {\n\t\t\tname: 'SearXNG',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Tools'],\n\t\t\t\tTools: ['Other Tools'],\n\t\t\t},\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolsearxng',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\tinputs: [],\n\t\toutputs: [NodeConnectionTypes.AiTool],\n\t\toutputNames: ['Tool'],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\tname: 'searXngApi',\n\t\t\t\trequired: true,\n\t\t\t},\n\t\t],\n\t\tproperties: [\n\t\t\tgetConnectionHintNoticeField([NodeConnectionTypes.AiAgent]),\n\t\t\t{\n\t\t\t\tdisplayName: 'Options',\n\t\t\t\tname: 'options',\n\t\t\t\ttype: 'collection',\n\t\t\t\tplaceholder: 'Add Option',\n\t\t\t\tdefault: {},\n\t\t\t\toptions: [\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Number of Results',\n\t\t\t\t\t\tname: 'numResults',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\tdefault: 10,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Search Page Number',\n\t\t\t\t\t\tname: 'pageNumber',\n\t\t\t\t\t\ttype: 'number',\n\t\t\t\t\t\tdefault: 1,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Language',\n\t\t\t\t\t\tname: 'language',\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tdefault: 'en',\n\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t'Defines the language to use. It\\'s a two-letter language code. (e.g., `en` for English, `es` for Spanish, or `fr` for French). Head to <a href=\"https://docs.searxng.org/user/search-syntax.html#select-language\">SearXNG search syntax page</a> for more info.',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tdisplayName: 'Safe Search',\n\t\t\t\t\t\tname: 'safesearch',\n\t\t\t\t\t\ttype: 'options',\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'None',\n\t\t\t\t\t\t\t\tvalue: 0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Moderate',\n\t\t\t\t\t\t\t\tvalue: 1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tname: 'Strict',\n\t\t\t\t\t\t\t\tvalue: 2,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdefault: 0,\n\t\t\t\t\t\tdescription: 'Filter search results of engines which support safe search',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tasync supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData> {\n\t\tconst credentials = await this.getCredentials<{ apiUrl: string }>('searXngApi');\n\t\tconst options = this.getNodeParameter('options', itemIndex) as Options;\n\n\t\tconst tool = new SearxngSearch({\n\t\t\tapiBase: credentials.apiUrl,\n\t\t\theaders: {\n\t\t\t\tAccept: 'application/json',\n\t\t\t},\n\t\t\tparams: options,\n\t\t});\n\n\t\treturn {\n\t\t\tresponse: logWrapper(tool, this),\n\t\t};\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAA8B;AAC9B,0BAAoC;AAQpC,wBAA2B;AAC3B,0BAA6C;AAStC,MAAM,YAAiC;AAAA,EAAvC;AACN,uBAAoC;AAAA,MACnC,aAAa;AAAA,MACb,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO,CAAC,WAAW;AAAA,MACnB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,UAAU;AAAA,QACT,MAAM;AAAA,MACP;AAAA,MACA,OAAO;AAAA,QACN,YAAY,CAAC,IAAI;AAAA,QACjB,eAAe;AAAA,UACd,IAAI,CAAC,OAAO;AAAA,UACZ,OAAO,CAAC,aAAa;AAAA,QACtB;AAAA,QACA,WAAW;AAAA,UACV,sBAAsB;AAAA,YACrB;AAAA,cACC,KAAK;AAAA,YACN;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MACA,QAAQ,CAAC;AAAA,MACT,SAAS,CAAC,wCAAoB,MAAM;AAAA,MACpC,aAAa,CAAC,MAAM;AAAA,MACpB,aAAa;AAAA,QACZ;AAAA,UACC,MAAM;AAAA,UACN,UAAU;AAAA,QACX;AAAA,MACD;AAAA,MACA,YAAY;AAAA,YACX,kDAA6B,CAAC,wCAAoB,OAAO,CAAC;AAAA,QAC1D;AAAA,UACC,aAAa;AAAA,UACb,MAAM;AAAA,UACN,MAAM;AAAA,UACN,aAAa;AAAA,UACb,SAAS,CAAC;AAAA,UACV,SAAS;AAAA,YACR;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,YACV;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,YACV;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,cACT,aACC;AAAA,YACF;AAAA,YACA;AAAA,cACC,aAAa;AAAA,cACb,MAAM;AAAA,cACN,MAAM;AAAA,cACN,SAAS;AAAA,gBACR;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,gBACR;AAAA,gBACA;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,gBACR;AAAA,gBACA;AAAA,kBACC,MAAM;AAAA,kBACN,OAAO;AAAA,gBACR;AAAA,cACD;AAAA,cACA,SAAS;AAAA,cACT,aAAa;AAAA,YACd;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA;AAAA,EAEA,MAAM,WAAuC,WAAwC;AACpF,UAAM,cAAc,MAAM,KAAK,eAAmC,YAAY;AAC9E,UAAM,UAAU,KAAK,iBAAiB,WAAW,SAAS;AAE1D,UAAM,OAAO,IAAI,oCAAc;AAAA,MAC9B,SAAS,YAAY;AAAA,MACrB,SAAS;AAAA,QACR,QAAQ;AAAA,MACT;AAAA,MACA,QAAQ;AAAA,IACT,CAAC;AAED,WAAO;AAAA,MACN,cAAU,8BAAW,MAAM,IAAI;AAAA,IAChC;AAAA,EACD;AACD;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg height="92mm" viewBox="0 0 92 92" width="92mm" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-40.921303 -17.416526)"><g fill="none"><circle cx="75" cy="92" r="0" stroke="#000" stroke-width="12"/><circle cx="75.921" cy="53.903" r="30" stroke="#3050ff" stroke-width="10"/><path d="m67.514849 37.91524a18 18 0 0 1 21.051475 3.312407 18 18 0 0 1 3.137312 21.078282" stroke="#3050ff" stroke-width="5"/></g><path d="m3.706 122.09h18.846v39.963h-18.846z" fill="#3050ff" transform="matrix(.69170581 -.72217939 .72217939 .69170581 0 0)"/></g></svg>
|